NPC Engine Documentation
Release 8 -- 17. 12. 2000
Index
0. Disclaimer
1. What the NPC Engine does
2. Quick how-to to include the NPC Engine in your game
3. Path movement and subway movement
4. Advanced features
5. Functions
6. NPC_Engine properties
7. Entry points
8. Notes
9. Demo Story
10. Changes to the NPC Engine
NPC Engine is a library extension for the Inform interactive fiction authoring system. It
is provided "as is". The author takes no responsibility whatsoever for any
malfunctioning of this code.
NPC Engine and this documentation are copyright (c) 1999, 2000 Volker Lanz. You may
use these files in your own games freely. Please do not alter any of the files and publish
or distribute these modified versions without the author's permission.
Parts of NPC_Wait.h were written by: Andrew Clover, L. Ross Raszewski and Nicholas
Daley.
Original concept for subway movement by Infocom. Thanks to Mike Berlyn for
help with this.
Contact the author via:
volker.lanz@gmx.net
Comments, bug reports, criticism, questions and suggestions are always welcome.
The NPC Engine initially was an integral part of my Inform version of Infocom's excellent
game DEADLINE. After the game was mostly finished, I began isolating the NPC
movement and description parts from the game and created this library extension
out of these.
If you would like to have NPCs in your game that are wandering around in a more sophisticated
manner than just moving them from one room to another each turn on a predefined path,
and if you want the player to have the opportunity of following NPCs, this one is the
right library extension for you.
Additionally, it offers these features:
-
Complex descriptions of NPCs moving somewhere in the distance:
Mrs. Robner is off to the north, heading towards you.
Mr. Baxter, off to the south, opens a door and ducks into a room
to the west.
and even
You can hear footsteps on the staircase.
-
Asking the game itself and other NPCs where an NPC is:
> MRS. ROBNER, WHERE IS GEORGE?
"I last saw him just a minute ago. I don't know where he went,
though."
or
> WHERE IS MS. DUNBAR?
You haven't seen her yet.
Note that your code will have to remove question marks from the player's input to make these
work.
-
Following NPCs you see moving in the distance.
...
Mr. McNabb, off to the northeast, leaves your view to the east.
> FOLLOW MCNABB
Orchard Path
...
Mr. McNabb is off to the east, heading to the southeast.
-
Looking through windows:
Guest Room
...
> LOOK THROUGH WINDOW
...
To the east, Mr. Baxter comes into sight from the south.
Mr. McNabb is to the east, examining his work.
-
Going to a room with a "GO TO" verb
You can add a "GO TO" verb to your game, thus allowing the player to go to
rooms without knowing which path he has to take for that.
Assuming that you already have some skeleton of a game, follow these steps to
integrate the NPC Engine into your story:
-
Include the npc_engine.h file in your main game file. Do this right after including
verblib.h. You probably know that the Inform Library is a bit picky about when you include
what, so make sure to include npc_engine.h right there.
-
Call the function NPC_Initialise() from your game's Initialise() routine.
-
Make all your NPCs inherit the NPC_Engine class.
-
Make all your rooms inherit the NPC_Room class.
-
Now you can move your NPCs by calling the NPC_Path(npc, destination) function.
An NPC object's npc_arrived() property may deal with his arrival at a given destination.
Beginning with release 6 of the NPC Engine, you can choose between two
different types of movement calculation: Path movement or subway movement.
-
Path movement is the old way of calculating NPC movements the NPC Engine
used in all versions prior to release 6 (Dijkstra's shortest path algorithm).
-
Subway movement is similar to the way Infocom did NPC movement in their games. It's
a bit more complicated, but may be up to 40% faster in games with a larger landscape
(in small games, like the demo game included, it can even be slower than normal path
finding). Subway movement means that a number of "subway lines" run through
the rooms in your game; NPCs can "ride" on these subway lines to get from
a source to a destination room.
The default movement type is path movement. To use subway movement, you need
to tell the NPC Engine that you intend to do so by setting the constant
"SUBWAY_MOVE_TYPE" before including npc_engine.h.
Additionally, you of course have to set the "subway lines" the NPCs
can "ride on" in your game. This is done using the npc_subway property
in the rooms in your game:
NPC_Room Front_Path "Front Path"
with
...
npc_subway pink_line red_line,
...
Here, the pink and red subway lines run through the room Front_Path. Note that one line
may only cross another line once and lines may not go in circles. There's a maximum number
of lines and a maximum number of intersections; both of these can easily be changed.
Have a look at the beginning of npc_engine.h for details on this.
-
Finding NPCs
The NPC Engine includes a "find npc" verb. If the player ever met Mrs. Robner
before,
> FIND MRS. ROBNER
will tell him when he last saw Mrs. Robner, or, if she is visible right now, tell him
where she is.
The player may also ask other NPCs where an NPC is:
> GEORGE, WHERE IS MRS. ROBNER
George will reply what he knows: If he met Mrs. Robner, say, an hour before, he'll say
"I last saw her about an hour ago. I don't know where she went, though." If
she's in the same room, he'll simply say "Ahem...". If he hasn't met her before,
he will reply "I haven't seen her today."
-
Following NPCs
The player may type
> FOLLOW MRS. ROBNER
if he can see her at the moment to follow her. The NPC Engine used to know three different types
of following in versions prior to version 6. Now, there is only one left,
which was called PATH_FOLLOW_TYPE before: The player will move one
location toward where he sees the NPC in question right now.
-
Looking through windows
The simple action of looking through a window actually was quite hard to implement. The
main problem with this is that if the player looks through a window, the response is
printed and then the NPCs are moved, thus the player always gets information about
how things were the last move. If accurate timing is important, this becomes unacceptable. The
solution I chose to this may seem a bit awkward at first: NPC_LookThroughWindow()
doesn't print anything, but simply sets a property for all NPCs to the window that
is being looked through. When the NPC is moved aftwards, this property is checked and a message is
printed if the NPC is visible through the window.
Follow these steps to allow looking through a window:
- Add a looks_to property to the window, listing all the rooms this window lets the player
see into:
...
looks_to Corridor_1 Corridor_2 Corridor_3 Corridor_4,
...
- Add a door_dir to the window, saying in which direction the windows looks:
...
door_dir e_to,
...
- Add a door_to property, saying which is the room directly on the other side of the
window:
...
door_to Corridor_4,
...
- Finally, call NPC_LookThroughWindow() in the before() property for ##Search or
##LookThrough or what have you:
...
before
[;
Search:
print "You can see the corridor from
here.^";
return (NPC_LookThroughWindow(self));
],
...
Because the NPC_LookThroughWindow() routine doesn't know yet if anything will be
printed at all, you should always print some text not related to the NPCs moving, like in
the example above. An NPC's daemon must be active to make this NPC visible through a
window if he is not moving. This is one of the reasons why NPC's daemons should never be
stopped as long as the NPCs are still on the visible playfield.
Your window must not rely on
NPC descriptions being printed! If no NPCs can be seen through it, nothing will be printed
at all!
|
-
Stopping NPCs on their path to somewhere
If you want some action of the player to stop the NPC temporarily on his path, you
can achieve this goal by setting the NPC's npc_stopped property to a value higher than
1. E.g., if you wish to stop Mrs. Robner when the player says
> HELLO MRS. ROBNER
you would add the following to her before() property (assuming you have a "hello"
verb in your game):
...
before
[;
...
Hello:
self.npc_stopped = 2;
"Mrs. Robner is facing you.";
...
],
...
This will stop Mrs. Robner from following her set path for two moves. The NPC Engine
will print a default message ("Mrs. Robner starts to move about distractedly.")
one move before she'll walk off again. If you want the player to be able to stop her
from moving by talking to her, you may wish to add a self.npc_stopped = n; to her life's
property ##Ask reaction, too.
-
Waiting for NPCs
In addition to the NPC_Engine.h header file, another header file called NPC_Wait.h
is included in this release of the NPC Engine. This one replaces waittime.h (by Andrew
Clover, L. Ross Raszewski and Nicholas Daley) for use together with NPC_Engine.h. Include
after Grammar.h in your story file, if you want to use it (you don't have to).
-
Altering the default NPC Engine messages
You can change any message the NPC Engine prints by replacing the routine NPC_Msg()
in npc_engine.h with your own, modified version. Every message printed by the NPC Engine
is in this routine (apart from error messages, which are all in NPC_Error(), and the
messages printed in npc_walkon(), npc_walkoff() and npc_path_blocked(), which are all
properties of the NPC_Engine class).
-
Using the "GO TO" verb
If you want players to be able to say "GO TO <room>" in your game to
move them one step closer to the given destination, you'll simply have to extend the
grammar for "go" in your game with something like this:
extend 'go'
* 'to' scope=NPC_Goto_Scope -> NPC_Goto;
Additionally, any room that the player may choose as a target for this must have
the attribute "npc_goto" and, of course, a name property.
-
NPC_Initialise ()
Used to initialise the NPC Engine. You must call this from your game's Initialise()
routine.
-
NPC_Move (npc, destination, action, object)
This moves an NPC to a destination, using a certain action and a certain object. It
also prints a description of the NPC moving. If you want to remove an NPC from your
game, call NPC_Move(npc, 0);.
-
NPC_Path (npc, destination)
Calculates and sets an NPC on its path to a destination.
This one may be replaced by game code, because it only calls another function called
NPC__Path() (note the two underscores). Replacing this is particularly useful if you
have a set of pre-defined paths that you want to be checked first and call NPC_PrePath()
if any of them can be used; only if none of them fits you then call NPC__Path().
Note: Calling NPC_Path with the arguments used in moveclas.h is valid. The arguments
ifblocked and use_best, however, don't do anything.
-
NPC_FindNextMove (npc, source_room, target_room)
Finds which room will bring the given NPC in source_room closer to his target,
target_room. This one will only work with subway line mode.
Returns the room the NPC should go to. Prints an error message if no path can be found.
-
NPC_PrePath (npc, array, number_of_entries)
This routine sets an NPC on a pre-defined path. You must pass an NPC, an array of stored
destinations (e.g. "e_obj, e_obj, n_obj" and so on) and the number of entries
in this array.
-
NPC_Schedule (npc, destination, delay)
Schedules the movement of an NPC to a certain destination using the NPC Engine's timer.
Give the delay in moves until you want the NPC to begin moving.
-
NPC_LookThroughWindow (window)
Used to look through windows. See Advanced Features.
-
NPC_GetOtherSide (room, direction)
Returns the room that lies in this direction from the given room. Tries to get through
doors, if any are in the way. Returns false if there's no room in the given direction.
-
NPC_GetDirFor (source_room, dest_room)
Returns the direction to go from source_room to dest_room if they are neighbours and
there's a direct connection between them, otherwise false
-
NPC_CheckVisible (test_room, from_room)
This will return the direction of visibility for the room test_room from the room (you
guessed it) from_room. If from_room is omitted, it defaults to the current location.
Returns false if test_room isn't visible at all.
-
NPC_Msg (action, message_number, object1, object2)
NPC_Msg() is used for printing all the NPC Engine messages. Replace it to modify the
messages, if you want to.
-
NPC_Error (error_number, object1, object2)
By replacing this routine with an empty stub-routine, you can keep the NPC Engine from
printing any error messages.
The following is a list of all the important properties each object of the NPC_Engine
class has:
-
npc_before_action()
Called each time before the movement of an NPC. If it returns 1, it has
printed something (and thus the player is asked whether he wants to keep
waiting). If it returns 2, movement is terminated (but npc_arrived() will not
be called). A return value of 3 means both of the two.
-
npc_after_action()
Called each time after the movement of an NPC. Should return true if it printed something
to help notify NPC_Wait.h.
-
npc_arrived (room)
This one is being called when the NPC arrives in its target room. It gets passed the room
the NPC is now in. Usually, this one is used to schedule the next path the NPC takes.
Should return true if it printed something.
-
npc_walkon()
Called when an NPC enters the room the player is in to notify of his movement.
Should return true if it printed something.
-
npc_walkoff()
Called when an NPC leaves the room the player is in to notify of his movement.
Should return true if it printed something.
-
npc_path_blocked (source, destination, blocking_door)
Called when an NPC can't go on on his path due to a locked door in his way
(or a door that is closed and doesn't provide an npc_door_open property).
Arguments are the room the NPC comes from, his destination room and the door
that is blocking the path. Note that the NPC has already been stopped when
this is called. This property may print a suitable message for the player and
decide what the NPC now wants to do. Should return true if it printed
something.
If this routine doesn't start a new movement, the NPC will stay in front of the
door, even if it is being unlocked later on. He will not try to walk
through the door on his own.
-
npc_door_open()
This is not a property of the NPC_Engine class. Instead, each door in your game that an NPC
may walk through needs to have this. It just has to open the door, if need be, and return
true if it did just that:
...
npc_open
[npc;
if (self hasnt locked)
{
npc.npc_door_passed = self;
give self open;
rtrue;
}
rfalse;
],
...
As you can see, the property also stores that it was opened in the npc_door_passed
property of that NPC. This is necessary to enable the NPC Engine to notify the player of this
door opening.
These are the entry points the NPC Engine calls. Replace any of these functions in your
own code to modify the behaviour of the engine.
Note: Some of these already do some stuff, so you should always copy the originals
from the npc_engine.h file and modify these instead of writing completely new functions.
-
NPC_AfterPrintVisible (npc, current_room)
Is called if the NPC Engine tried to print a movement description for an NPC, but couldn't
since it concluded that the NPC is not visible to the player. This one could be used for
"You can hear footsteps on the staircase." stuff. Arguments passed are the NPC
in question and the room he is in. Must return true if it printed something.
-
NPC_BeforeCheckVisible (current_room, test_room)
Before the visibility of a certain room from a certain location is checked, this routine
is called. If it returns true, the NPC Engine will assume the room is not visible.
current_room is the room the visibility is checked from, test_room is the room for which
it is checked.
-
NPC_BeforeFollowing (npc)
Is called before an NPC is followed. If it returns true, following is not allowed.
-
NPC_AfterFollowing (npc, previous_location_of_player, success)
After following an NPC, this routine is called. Arguments are the NPC in question, the
previous location of the player before following and 1, if the following was successful or
0 if not.
Always use cut and paste when
replacing any of these entry points!
|
-
The maximum number of NPCs the NPC Engine can deal with is 32.
-
The NPC Engine uses the LookRoutine() Inform Library entry point. If you need
this entry point in your game, delete it in npc_engine.h and call NPC_LookRoutine() from your
story's entry point.
-
The NPC Engine doesn't care whether you use time or moves in your story. The standard
replies to the "where is"/"find" question, however, use one turn for
one minute. If time passes quicker or slower in your game, you will have to alter
this.
-
If you use calculated paths extensively, there may be certain delays in
your game on slower machines, i.e. very old computers and
handhelds.
-
The player can never see through doors, may they be open or closed
("it's not a bug, it's a feature").
-
No descriptions are printed for NPCs that aren't moving. The NPC's own describe property
may do that. On the other hand, while NPCs are moving, their own describe
property should make sure it doesn't print descriptions to avoid output like
this:
Living Room
...
Mrs. Robner is sitting here knitting.
...
Mrs. Robner heads off to the east.
...
An NPC's describe property can achieve this with the following two lines at
its beginning:
if (self.npc_move_type == 1 or 3 or 4 && self.npc_stopped == 0)
rtrue;
-
The NPC Engine does have some support for pluralname objects in its
default messages, but beware: Some messages rely on object names beginning with a capital
letter, thus having an NPC like "seven grues" isn't a particularly
good idea, because you'll get output like this:
Darkness
It's dark and you can't see a thing.
seven grues step into the room from the west.
-
If you want no description printed for an NPC, give him the npc_hidden
attribute. Note that this does not affect what npc_walkon(), npc_walkoff()
and describe() do, it will only keep NPC_PrintVisible() from printing things
like "Off to the east, Mr. McNabb disappears from sight to the
south."
This release of the NPC Engine includes a small demo story in source and binary format
to show the use of the engine. Note that, although this demo game features rooms from the
game INTRUDER (which can be downloaded from ftp.gmd.de/if-archive/games/zcode/intruder.zip),
it doesn't have any objects other than the rooms themselves (apart from a key to
lock the only door that is there). Also, the NPCs aren't from
INTRUDER.
If you want to see the NPC Engine really shine, you should have a look
at the source code for the Inform version of DEADLINE. This source code is
available from: ftp.gmd.de/if-archive/games/source/inform/Deadline.zip.
8.001217 |
The NPC Engine doesn't use the Inform Library's DEBUG constant
anymore; define a "NPC_DEBUG" constant if you want to get
debugging information.
A number of small bugs fixed (thanks to James Hayes for help with
this).
If no path or next move was found, the NPC Engine will no longer
print any error message; the functions will simply return false. The
game itself has to deal with cases where it called the functions inside
the Engine and may not rely on the Engine always returning a room
or finding a path.
Follow-mode for NPCs added, so that NPCs can be set to follow the player.
GO TO verb added.
|
7.000606 |
Various little fixes and modifications to make the NPC Engine run a bit faster;
games that use it are still nearly unplayable on a PDA, however (about 3 seconds
per minute on an overclocked Palm IIIc).
Subway movement nearly completely rewritten; is now up to 40% faster than
calculated paths.
The NPC_Engine object isn't a daemon anymore, but a floating object that is
present in all rooms. This may seem a bit strange, but it ensures that the
Inform Library always executes any daemons and timers before the NPCs
are being moved and described.
No longer does the NPC Engine use the time_out and daemon properties of the
Inform Library. You can now use timers and daemons in NPC Engine objects as
you like.
Can now set the order in which NPCs are processed by manually setting their
number and npc_next properties; must define NPC_MANUAL_NUMBERS before including
npc_engine.h.
|
6.000111 |
No longer uses the NewRoom() entry point.
Only one type of following is supported from now on (formerly called PATH_follow_type).
New attribute: give an NPC the npc_hidden attribute and the NPC Engine will
not print any descriptions of this NPC as long as it isn't in the same room
as the player.
New property: npc_path_blocked() is called when an NPC can't go on because
of a locked door in his way.
No modification of the player object required anymore.
Some support for pluralname objects (see notes)
NPCs are now in scope as long as they're visible to the player.
Subway movement type added.
Moving NPCs are no longer concealed; the NPC's describe() property has to
deal itself with not printing a description then.
May now use routines or strings in (dir)_to properties, e.g.
...
e_to "There is only a short blind corridor there.",
...
Doors may use "location" to check where they are (though this is
bad style and therefore not recommended).
|
5.991122 |
New NPC_Msg() entry allows replacing standard
message for "find" verb.
npc_before_action() is now only called if the NPC does indeed move.
NPC_Wait.h now comes with the NPC Engine to allow for complex waiting.
Bug fixed with stopping NPCs that suddenly weren't described anymore when
they tried to leave.
Bug fixed in NPC_Move's call to PrintWindowVisible.
More small bug fixes.
This document created, replacing the old text file manual.
|
3.990531 |
Now works with Inform 6.21 and Library 6/9; complies
to strict error checking rules
Minor modifications and enhancements to the manual
Demo game added
NPC Engine now uses TestScope() instead of comparing ScopeCeiling()s
where possible.
|
1.990423 |
First public release |